﻿
Imports std
Imports storoomc
Imports storcore
Imports storcore.storoomDistrTM

'treat each storoom/stornode managed by storoom as the same, write / modify / delete, etc to all.
'and read from a single node
Public Class nodes_redundance
    Inherits storoomOper(Of storoomDistrTM)

    Public Overrides Function showslice(ByVal slice As storageInterface) As String
        Return common.showslice(slice)
    End Function

    Protected Overridable Function slicehealth(ByVal slice As storageInterface) As Boolean
        Dim hslice As storageInterface = Nothing
        Dim node As stornode = Nothing
        node = convertor.cast(Of stornode)(slice)

        Dim rndstart As Int64
        rndstart = rnd(0, slices.size(), True)
        Dim i As Int64
        i = rndstart
        Dim found As Boolean = False
        Do
            'though the input slice should not be health slice during slicehealth call,
            'but to make it safer, add this chek
            If Not Object.ReferenceEquals(slices(i), slice) Then
                hslice = slices(i)
                If ishealthSlice(hslice) AndAlso hslice.sync(node.host, node.port) Then
                    raiseError("found an slice returns health, finish sync.")
                    found = True
                    Exit Do
                End If
            End If

            i += 1
            If i >= slices.size() Then
                i = 0
            End If
        Loop Until i = rndstart

        If Not found Then
            raiseError("cannot get a health slice to sync to failed slice.", errorHandle.errorType.exclamation)
            Return False
        Else
            Return True
        End If
    End Function

    Protected Sub startImpl(ByVal forceDisableCache As Boolean)
        If forceDisableCache Then
            enablecache = False
        End If
        MyBase.start()
        'manually call a slicesCleaner, to set unhealth slice. before each stornode ready to serve.
        slicesCleanerImpl()

        'sleep until normal slices count >= 1/2
        Dim sc As Int64 = 0
        Dim usc As Int64 = 0
        While True
            status(sc, usc)
            If sc = 0 OrElse Convert.ToDouble(usc) / sc > 1 / 2 Then
                sleepSeconds(30)
            Else
                Exit While
            End If
        End While

        sliceHealthHandle = AddressOf slicehealth
    End Sub

    Public Overrides Sub start()
        nodesStart(Me)
        'force disable cache in redundancy mode, as the sync operation is async.
        'before all data has been synced to broken stornode, storoom will think the stornode is health.
        startImpl(True)
    End Sub

    Protected MustInherit Class wmInfoBase(Of valueT)
        Inherits distrInfo(Of pair(Of wmtype, pair(Of String, pointer(Of valueT))), Boolean)
        Protected key As String = Nothing
        Protected v As valueT = Nothing
        Protected wm As wmtype

        Public Enum wmtype
            write
            modify
        End Enum

        Public Overrides Function finishOperation(ByRef rtn As Boolean) As Boolean
            rtn = rtn OrElse (result = ternary.true)
            Return True
        End Function

        Protected Overrides Function initialInfo(ByVal index As Int32, _
                                                 ByVal arg As pair(Of wmtype,  _
                                                                      pair(Of String, pointer(Of valueT)))) As Boolean
            If MyBase.initialInfo(index, arg) Then
                wm = arg.first
                copy(key, arg.second.first)
                v = +(arg.second.second)
                Return True
            Else
                Return False
            End If
        End Function
    End Class

    Private Class wmInfo
        Inherits wmInfoBase(Of bytes)

        Public Overrides Function finishOperation(ByRef rtn As Boolean) As Boolean
            rtn = rtn OrElse (result = ternary.true)
            Return True
        End Function

        Protected Overrides Function operationImpl() As ternary
            Dim rst As Boolean
            If wm = wmInfoBase(Of bytes).wmtype.write Then
                rst = slice.write(key, v)
            Else
                rst = slice.modify(key, v)
            End If
            Return ifoper(rst, ternary.true, ternary.false)
        End Function

        Public Shared Function createInfo() As wmInfo
            Return New wmInfo()
        End Function
    End Class

    Protected Function wm_redundancy(Of valueT)(ByVal wm As wmInfoBase(Of valueT).wmtype, _
                                                ByVal key As String, ByVal v As valueT, _
                                                ByVal slices As vector(Of storageInterface), _
                                                ByVal createInfo As wmInfoBase(Of valueT).createInfoDelegate, _
                                                Optional ByRef rtn As Boolean = False) _
                                                As Boolean
        If Not tm.distrOperation(Of wmInfoBase(Of valueT), 
                                      pair(Of wmInfoBase(Of valueT).wmtype, 
                                      pair(Of String, pointer(Of valueT))),
                                      Boolean) _
                                  (make_pair(wm, make_pair(key, New pointer(Of valueT)(v))),
                                   rtn,
                                   createInfo,
                                   slices) Then
            raiseError("cannot finish distrOperation when wm_redundancy", errorHandle.errorType.exclamation)
            rtn = False
        End If

        Return rtn
    End Function

    Protected Overridable Function write_redundancy(ByVal key As String, ByVal buff As bytes, _
                                                    ByVal slices As vector(Of storageInterface)) As Boolean
        Return wm_redundancy(wmInfo.wmtype.write, key, buff, slices, AddressOf wmInfo.createInfo)
    End Function

    Protected NotOverridable Overrides Function writeImpl(ByVal key As String, ByVal buff As bytes, _
                                                          ByRef slice As storageInterface, _
                                                          ByVal slices As vector(Of storageInterface)) As Boolean
        Return write_redundancy(key, buff, slices)
    End Function

    Protected Overridable Function modify_redundancy(ByVal key As String, ByVal buff As bytes, _
                                                     ByVal slices As vector(Of storageInterface)) As Boolean
        Return wm_redundancy(wmInfo.wmtype.modify, key, buff, slices, AddressOf wmInfo.createInfo)
    End Function

    Protected NotOverridable Overrides Function modifyImpl(ByVal key As String, ByVal buff As bytes, _
                                                           ByRef slice As storageInterface, _
                                                           ByVal slices As vector(Of storageInterface)) As Boolean
        Return modify_redundancy(key, buff, slices)
    End Function

    Protected Overrides Function selectSlices(ByVal key As String) As vector(Of storageInterface)
        Dim rtn As vector(Of storageInterface) = Nothing
        rtn = New vector(Of storageInterface)()
        rtn.push_back(randomSlice(slices, Nothing))
        Return rtn
    End Function

    Private Delegate Function randomworkDelegate(Of argType)(ByVal slice As storageInterface, _
                                                             ByRef arg As argtype) As Boolean

    Private Function randomwork(Of argType)(ByVal work As randomworkDelegate(Of argType), _
                                            ByRef arg As argType, _
                                            ByVal slices As vector(Of storageInterface), _
                                            ByVal workname As String) As ternary
        assert(Not work Is Nothing, "work is nothing.")

        Dim slice As storageInterface = Nothing
        Dim index As Int64
        slice = randomSlice(slices, index)

        If slice Is Nothing Then
            raiseError("cannnot get an health slice to " + workname + ".", errorHandle.errorType.exclamation)
            Return ternary.unknown
        Else
            Return ifoper(work(slice, arg), ternary.true, ternary.false)
        End If
    End Function

    Private Function emptywork(ByVal slice As storageInterface, ByRef arg As Boolean) As Boolean
        assert(Not slice Is Nothing, "slice is nothing.")
        Return slice.empty()
    End Function

    Protected Overrides Function emptyImpl(ByVal slices As vector(Of storageInterface)) As ternary
        Return randomwork(AddressOf emptywork, True, slices, "check empty")
    End Function

    Protected Overridable Function listwork(ByVal slice As storageInterface, ByRef arg As vector(Of String)) As Boolean
        assert(Not slice Is Nothing, "slice is nothing.")
        Return slice.list(arg)
    End Function

    Protected NotOverridable Overrides Function listImpl(ByVal l As vector(Of String), _
                                                         ByVal slices As vector(Of storageInterface)) As Boolean
        Return randomwork(AddressOf listwork, l, slices, "list") = ternary.true
    End Function

    Protected Overrides Function fullImpl(ByVal slices As vector(Of storageInterface)) As ternary
        Return tm.shareTypeFullImpl(slices)
    End Function

    Protected Overrides Function capacityImpl(ByRef cap As UInt64, _
                                              ByVal slices As vector(Of storageInterface)) As Boolean
        Return tm.shareTypeCapacityImpl(cap, slices)
    End Function

    Private Function valuesizework(ByVal slice As storageInterface, ByRef arg As UInt64) As Boolean
        assert(Not slice Is Nothing, "slice is nothing.")
        Return slice.valuesize(arg)
    End Function

    Protected Overrides Function valuesizeImpl(ByRef sz As UInt64, _
                                               ByVal slices As vector(Of storageInterface)) As Boolean
        Return randomwork(AddressOf valuesizework, sz, slices, "valuesize") = ternary.true
    End Function

    Protected Overridable Function keycountwork(ByVal slice As storageInterface, ByRef count As UInt64) As Boolean
        assert(Not slice Is Nothing, "slice is nothing.")
        Return slice.keycount(count)
    End Function

    Protected NotOverridable Overrides Function keycountImpl(ByRef count As UInt64, _
                                                             ByVal slices As vector(Of storageInterface)) As Boolean
        Return randomwork(AddressOf keycountwork, count, slices, "keycount") = ternary.true
    End Function

    Private Function syncwork(ByVal slice As storageInterface, ByRef arg As pair(Of String, Int32)) As Boolean
        assert(Not slice Is Nothing, "slice is nothing.")
        Return synchelper(slice, arg.first, arg.second)
    End Function

    Protected Overrides Function syncImpl(ByVal host As String, ByVal port As Int32, _
                                          ByVal slices As vector(Of storageInterface)) As Boolean
        Return randomwork(AddressOf syncwork, make_pair(host, port), slices, "sync") = ternary.true
    End Function
End Class
